﻿using Castle.Core.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WJP.Events.Bus.Factories;
using WJP.Events.Bus.Handlers;

namespace WJP.Events.Bus
{
    /// <summary>
    /// 事件总线
    /// </summary>
    public class EventBus : IEventBus
    {
        #region 属性、构造方法

        /// <summary>
        /// 获取默认的<see cref="EventBus"/>实例
        /// </summary>
        public static EventBus Default { get; } = new EventBus();

        /// <summary>
        /// 日志记录器的引用。
        /// </summary>
        public ILogger Logger { get; set; }

        /// <summary>
        /// 所有注册的句柄工厂
        /// <para>Key: 事件类型</para>
        /// <para>Value: 句柄工厂列表</para>
        /// </summary>
        private readonly ConcurrentDictionary<Type, List<IEventHandlerFactory>> _handlerFactories;

        /// <summary>
        /// 创建一个新的<see cref="EventBus"/>实例
        /// <para>使用全局的<see cref="Default"/>代替创建一个新实例</para>
        /// </summary>
        public EventBus()
        {
            Logger = NullLogger.Instance;
            _handlerFactories = new ConcurrentDictionary<Type, List<IEventHandlerFactory>>();
        }

        #endregion

        #region Register

        /// <summary>
        /// 注册一个事件。
        /// <para>给定的动作被调用所有事件发生。</para>
        /// </summary>
        /// <typeparam name="TEventData">事件类型</typeparam>
        /// <param name="action"></param>
        /// <returns></returns>
        public IDisposable Register<TEventData>(Action<TEventData> action) where TEventData : IEventData
        {
            return Register(typeof(TEventData), new ActionEventHandler<TEventData>(action));
        }

        /// <summary>
        /// 注册一个事件。
        /// <para>给定的动作被调用所有事件发生。</para>
        /// </summary>
        /// <param name="eventType">事件类型</param>
        /// <param name="handler">处理事件的对象</param>
        /// <returns></returns>
        public IDisposable Register(Type eventType, IEventHandler handler)
        {
            return Register(eventType, new SingleInstanceHandlerFactory(handler));
        }

        /// <summary>
        /// 注册一个事件
        /// </summary>
        /// <param name="eventType">事件类型</param>
        /// <param name="factory">一个创建/释放处理句柄的工厂</param>
        /// <returns></returns>
        public IDisposable Register(Type eventType, IEventHandlerFactory factory)
        {
            GetOrCreateHandlerFactories(eventType)
                .Locking(factories => factories.Add(factory));

            return new FactoryUnregistrar(this, eventType, factory);
        }

        #endregion

        #region Unregister

        /// <summary>
        /// 注销一个事件
        /// </summary>
        /// <typeparam name="TEventData">事件类型</typeparam>
        /// <param name="action"></param>
        public void Unregister<TEventData>(Action<TEventData> action) where TEventData : IEventData
        {
            GetOrCreateHandlerFactories(typeof(IEventData))
                .Locking(factories =>
                {
                    factories.RemoveAll(
                        factory =>
                        {
                            var singleInstanceFactory = factory as SingleInstanceHandlerFactory;
                            if (singleInstanceFactory == null)
                                return false;

                            var actionHandler = singleInstanceFactory.HandlerInstance as ActionEventHandler<TEventData>;
                            if (actionHandler == null)
                                return false;

                            return actionHandler.Action == action;
                        });
                });
        }

        /// <summary>
        /// 注销一个事件
        /// </summary>
        /// <param name="eventType">事件类型</param>
        /// <param name="handler">之前注册过的处理事件的对象</param>
        public void Unregister(Type eventType, IEventHandler handler)
        {
            GetOrCreateHandlerFactories(eventType)
                .Locking(factories =>
                {
                    factories.RemoveAll(
                        factory =>
                            factory is SingleInstanceHandlerFactory &&
                            (factory as SingleInstanceHandlerFactory).HandlerInstance == handler
                        );
                });
        }

        /// <summary>
        /// 注销一个事件
        /// </summary>
        /// <param name="eventType">事件类型</param>
        /// <param name="factory">事件工厂</param>
        public void Unregister(Type eventType, IEventHandlerFactory factory)
        {
            GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Remove(factory));
        }

        #endregion

        #region Trigger

        /// <summary>
        /// 触发一个事件
        /// </summary>
        /// <typeparam name="TEventData">事件类型</typeparam>
        /// <param name="eventData">事件相关数据</param>
        public void Trigger<TEventData>(TEventData eventData) where TEventData : IEventData
        {
            Trigger((object)null, eventData);
        }

        /// <summary>
        /// 触发一个事件
        /// </summary>
        /// <typeparam name="TEventData">事件类型</typeparam>
        /// <param name="eventSource">事件来源</param>
        /// <param name="eventData">事件相关数据</param>
        public void Trigger<TEventData>(object eventSource, TEventData eventData)
            where TEventData : IEventData
        {
            Trigger(typeof(TEventData), eventSource, eventData);
        }

        /// <summary>
        /// 触发一个事件
        /// </summary>
        /// <param name="eventType">事件类型</param>
        /// <param name="eventData">事件相关数据</param>
        public void Trigger(Type eventType, IEventData eventData)
        {
            Trigger(eventType, null, eventData);
        }

        /// <summary>
        /// 触发一个事件
        /// </summary>
        /// <param name="eventType">事件类型</param>
        /// <param name="eventSource">事件来源</param>
        /// <param name="eventData">事件相关数据</param>
        public void Trigger(Type eventType, object eventSource, IEventData eventData)
        {
            var exceptions = new List<Exception>();

            eventData.EventSource = eventSource;

            foreach(var handlerFactories in GetHandlerFactories(eventType))
            {
                foreach(var handlerFactory in handlerFactories.EventHandlerFactories)
                {
                    var handlerType = handlerFactory.GetHandlerType();

                    if (IsEventHandler(handlerType))
                    {
                        TriggerHandlingException(handlerFactory, handlerFactories.EventType, eventData, exceptions);
                    }
                    else
                    {
                        var message = $"Event handler to register for event type {eventType.Name} does not implement IEventHandler<{eventType.Name}> or IAsyncEventHandler<{eventType.Name}> interface!";
                        exceptions.Add(new Exception(message));
                    }
                }
            }
        }

        #endregion

        #region 方法

        /// <summary>
        /// 获取或者创建事件类型的工厂列表
        /// </summary>
        /// <param name="eventType">事件类型</param>
        /// <returns></returns>
        private List<IEventHandlerFactory> GetOrCreateHandlerFactories(Type eventType)
        {
            return _handlerFactories.GetOrAdd(eventType, (p) => new List<IEventHandlerFactory>());
        }

        private IEnumerable<EventTypeWithEventHandlerFactories> GetHandlerFactories(Type eventType)
        {
            var handlerFactoryList = new List<EventTypeWithEventHandlerFactories>();

            foreach (var handlerFactory in _handlerFactories.Where(hf => ShouldTriggerEventForHandler(eventType, hf.Key)))
            {
                handlerFactoryList.Add(new EventTypeWithEventHandlerFactories(handlerFactory.Key, handlerFactory.Value));
            }

            return handlerFactoryList.ToArray();
        }

        private static bool ShouldTriggerEventForHandler(Type eventType, Type handlerType)
        {
            if (handlerType == eventType)
                return true;

            if (handlerType.IsAssignableFrom(eventType))
                return true;

            return false;
        }

        /// <summary>
        /// 是不是IEventHandler泛型类型
        /// </summary>
        /// <param name="handlerType"></param>
        /// <returns></returns>
        private bool IsEventHandler(Type handlerType)
        {
            return handlerType.GetInterfaces()
                .Where(p => p.IsGenericType)
                .Any(p => p.GetGenericTypeDefinition() == typeof(IEventHandler<>));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="handlerFactory"></param>
        /// <param name="eventType"></param>
        /// <param name="eventData"></param>
        /// <param name="exceptions"></param>
        private void TriggerHandlingException(IEventHandlerFactory handlerFactory, Type eventType, IEventData eventData, List<Exception> exceptions)
        {
            var eventHandler = handlerFactory.GetHandler();
            try
            {
                if (eventHandler == null)
                    throw new ArgumentNullException("事件类型{0}的注册事件处理程序为null".FormatArgs(eventType.Name));

                var handlerType = typeof(IEventHandler<>).MakeGenericType(eventType);

                var method = handlerType.GetMethod(
                    "HandlerEvent",
                    new[] { eventType });

                method.Invoke(eventHandler, new object[] { eventData });
            }
            catch (Exception ex)
            {
                exceptions.Add(ex);
            }
            finally
            {
                handlerFactory.ReleaseHandler(eventHandler);
            }
        }

        #endregion

        #region 内部类

        /// <summary>
        /// 
        /// </summary>
        private class EventTypeWithEventHandlerFactories
        {
            public Type EventType { get; }

            public List<IEventHandlerFactory> EventHandlerFactories { get; }

            public EventTypeWithEventHandlerFactories(Type eventType, List<IEventHandlerFactory> eventHandlerFactories)
            {
                EventType = eventType;
                EventHandlerFactories = eventHandlerFactories;
            }
        }

        #endregion
    }
}
